﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace Qadir
{
    /// <summary>
    /// A static class allowing for EASY asynchronous execution of code, even in .NET 2.0.
    /// </summary>
    public static class Async
    {
        /// <summary>
        /// A method that accepts no parameters, and whose return value is void.
        /// </summary>
        public delegate void AsyncVoid();

        /// <summary>
        /// A method whose return value is void.
        /// </summary>
        /// <param name="arguments">Any and all arguments that the method accepts.</param>
        public delegate void AsyncVoidWithParameters(object[] arguments = null);

        /// <summary>
        /// A method that accepts no parameters, and whose return value is of type T.
        /// </summary>
        /// <typeparam name="T">Any .NET type, managed or unmanaged.</typeparam>
        /// <returns>Returns a T after running the code within the delegate.</returns>
        public delegate T AsyncTask<T>();

        /// <summary>
        /// A method whose return value is of type T.
        /// </summary>
        /// <typeparam name="T">Any .NET type, managed or unmanaged.</typeparam>
        /// <param name="arguments">Any and all arguments that the method accepts.</param>
        /// <returns>Returns a T after running the code within the delegate.</returns>
        public delegate T AsyncTaskWithParameters<T>(object[] arguments = null);

        /// <summary>
        /// Asynchronously executes code.
        /// </summary>
        /// <param name="blockUntilComplete">A boolean value that determines if the method waits until the method is completed before returning.</param>
        public static void GoVoid(AsyncVoid run, bool blockUntilComplete = true)
        {
            ManualResetEvent signal = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem((o) => { run(); ((ManualResetEvent)signal).Set(); }, signal);
            if (blockUntilComplete)
                WaitHandle.WaitAll(new WaitHandle[] { signal });
        }

        /// <summary>
        /// Asynchronously executes code.
        /// </summary>
        /// <param name="run">A method whose return value is void.</param>
        /// <param name="arguments">Any and all arguments that the method accepts.</param>
        /// <param name="blockUntilComplete">A boolean value that determines if the method waits until the method is completed before returning.</param>
        public static void GoVoid(AsyncVoidWithParameters run, object[] arguments = null, bool blockUntilComplete = true)
        {
            ManualResetEvent signal = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem((o) => { run(arguments); ((ManualResetEvent)signal).Set(); }, signal);
            if (blockUntilComplete)
                WaitHandle.WaitAll(new WaitHandle[] { signal });
        }

        /// <summary>
        /// Asynchronously executes code.
        /// </summary>
        /// <param name="run">A method whose return value is of type T.</param>
        /// <param name="blockUntilComplete">A boolean value that determines if the method waits until the method is completed before returning.</param>
        public static void Go(AsyncTask<object> run, bool blockUntilComplete = true)
        {
            ManualResetEvent signal = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem((o) => { run(); ((ManualResetEvent)signal).Set(); }, signal);
            if (blockUntilComplete)
                WaitHandle.WaitAll(new WaitHandle[] { signal });
        }

        /// <summary>
        /// Asynchronously executes code.
        /// </summary>
        /// <param name="run">A method whose return value is of type T.</param>
        /// <param name="arguments">Any and all arguments that the method accepts.</param>
        /// <param name="blockUntilComplete">A boolean value that determines if the method waits until the method is completed before returning.</param>
        public static void Go(AsyncTaskWithParameters<object> run, object[] arguments = null, bool blockUntilComplete = true)
        {
            ManualResetEvent signal = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem((o) => { run(arguments); ((ManualResetEvent)signal).Set(); }, signal);
            if (blockUntilComplete)
                WaitHandle.WaitAll(new WaitHandle[] { signal });
        }

        /// <summary>
        /// Asynchronously executes code.
        /// </summary>
        /// <typeparam name="T">Any .NET type, managed or unmanaged.</typeparam>
        /// <param name="run">A method whose return value is of type T.</param>
        /// <param name="blockUntilComplete">A boolean value that determines if the method waits until the method is completed before returning.</param>
        /// <returns>Returns a T after asynchronous method execution, or default(T).</returns>
        public static T Go<T>(AsyncTask<T> run, bool blockUntilComplete = true)
        {
            T result = default(T);
            ManualResetEvent signal = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem((o) => { result = run(); ((ManualResetEvent)signal).Set(); }, signal);
            if (blockUntilComplete)
                WaitHandle.WaitAll(new WaitHandle[] { signal });
            return result;
        }

        /// <summary>
        /// Asynchronously executes code.
        /// </summary>
        /// <typeparam name="T">Any .NET type, managed or unmanaged.</typeparam>
        /// <param name="run">A method whose return value is of type T.</param>
        /// <param name="arguments">Any and all arguments that the method accepts.</param>
        /// <param name="blockUntilComplete">A boolean value that determines if the method waits until the method is completed before returning.</param>
        /// <returns>Returns a T after asynchronous method execution, or default(T).</returns>
        public static T Go<T>(AsyncTaskWithParameters<T> run, object[] arguments = null, bool blockUntilComplete = true)
        {
            T result = default(T);
            ManualResetEvent signal = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem((o) => { result = run(arguments); ((ManualResetEvent)signal).Set(); }, signal);
            if (blockUntilComplete)
                WaitHandle.WaitAll(new WaitHandle[] { signal });
            return result;
        }
    }
}
